#include "gobang.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define BOARD_WIDTH 19

typedef struct step_coord{
    uint16_t x;
    uint16_t y;
}step_coord;

#define LAST_STEP_MAX 3

struct gobang_state{
    uint32_t has_piece[BOARD_WIDTH]; //每一位表示该位置是否有棋子
    uint32_t is_white[BOARD_WIDTH]; //每一位表示该位置是否为白子
    step_coord last_step[LAST_STEP_MAX]; //保存最后几步，用于悔棋
    uint8_t last_step_count; 
    uint8_t state;
};


gobang_state* gobang_init(){
    gobang_state* s = malloc(sizeof(gobang_state));
    gobang_reset(s);
    return s;
}

void gobang_destroy(gobang_state* s){
    if(s)free(s);
}

int gobang_reset(gobang_state* s){
    if(s){
        memset(s,0,sizeof(gobang_state));
        s->state = GOBANG_STATE_BLACK_STEP;
    }
    return 0;
}

static int get_bit(uint32_t* p, int x, int y){
    uint32_t v = p[x];
    int ret = (v >> y) & 1;
    return ret;
}

static void set_bit(uint32_t* p, int x, int y){
    uint32_t v = p[x];
    v |= 1 << y;
    p[x] = v;
}

static void unset_bit(uint32_t* p, int x, int y){
    uint32_t v = p[x];
    v &= ~(1 << y);
    p[x] = v;
}

//0=黑子 1=白子 其他=-1
static int get_piece(gobang_state* s, int x, int y){
    if(x < 0 || x >= BOARD_WIDTH)return -1;
    if(y < 0 || y >= BOARD_WIDTH)return -1;
    if(!get_bit(s->has_piece, x, y))return -1;
    return get_bit(s->is_white, x, y);
}

static int check_win(gobang_state* s, int x, int y){
    int i;   
    int count[4] = {0};
    int stop[8] = {0};
    int exp = get_piece(s, x, y);    
    for (i = 1; i <= 4; i++){
        if(!stop[0] && get_piece(s, x - i, y) == exp){count[0] ++;}else{stop[0]=1;};
        if(!stop[1] && get_piece(s, x + i, y) == exp){count[0] ++;}else{stop[1]=1;};
        if(count[0] >= 4)return 1;
        if(!stop[2] && get_piece(s, x, y - i) == exp){count[1] ++;}else{stop[2]=1;};
        if(!stop[3] && get_piece(s, x, y + i) == exp){count[1] ++;}else{stop[3]=1;};
        if(count[1] >= 4)return 1;
        if(!stop[4] && get_piece(s, x - i, y - i) == exp){count[2] ++;}else{stop[4]=1;};
        if(!stop[5] && get_piece(s, x + i, y + i) == exp){count[2] ++;}else{stop[5]=1;};
        if(count[2] >= 4)return 1;
        if(!stop[6] && get_piece(s, x - i, y + i) == exp){count[3] ++;}else{stop[6]=1;};
        if(!stop[7] && get_piece(s, x + i, y - i) == exp){count[3] ++;}else{stop[7]=1;};
        if(count[3] >= 4)return 1;
    }
    return 0;    
}


int gobang_put_piece(gobang_state* s, int x, int y){
    if(!s)return -1;
    if(s->state >= 0x10)return GOBANG_ERROR_ALREADY_WIN;
    if(x < 0 || x > BOARD_WIDTH)return GOBANG_ERROR_OUT_BOARD;
    if(y < 0 || y > BOARD_WIDTH)return GOBANG_ERROR_OUT_BOARD;
    if(get_bit(s->has_piece, x, y))return GOBANG_ERROR_ALREADY_PIECE;
    set_bit(s->has_piece, x, y);
    if(s->state == GOBANG_STATE_WHITE_STEP){
        set_bit(s->is_white, x, y);        
    }
    if(check_win(s, x, y)){
        s->state |= 0x10;
    }else{
        s->state = (s->state == GOBANG_STATE_WHITE_STEP)?GOBANG_STATE_BLACK_STEP:GOBANG_STATE_WHITE_STEP;
    }
    //记录最后几步，用于悔棋
    if(s->last_step_count < LAST_STEP_MAX){
        s->last_step[s->last_step_count].x = x;
        s->last_step[s->last_step_count].y = y;
        s->last_step_count ++;
    }else{
        memmove(&(s->last_step[0]), &(s->last_step[1]), sizeof(step_coord)*(LAST_STEP_MAX - 1));
        s->last_step[LAST_STEP_MAX - 1].x = x;
        s->last_step[LAST_STEP_MAX - 1].y = y;    
    }
    return s->state;
}

int gobang_get_state(gobang_state* s){
    if(!s)return -1;
    return s->state;
}


int gobang_enum_pieces(gobang_state* s, gobang_enum_pieces_callback cb, void* udata){
    int x,y;
    if(!s)return -1;
    for (x = 0; x < BOARD_WIDTH; x++){
        for (y = 0; y < BOARD_WIDTH; y++){
            if(get_bit(s->has_piece, x, y)){
                cb(udata, x, y, get_bit(s->is_white, x, y));
            }
        }
    }
    return 0;
}

int gobang_has_piece(gobang_state* s, int x, int y){
    if(!s)return 0;
    if(x < 0 || x > BOARD_WIDTH)return GOBANG_ERROR_OUT_BOARD;
    if(y < 0 || y > BOARD_WIDTH)return GOBANG_ERROR_OUT_BOARD;    
    if(get_bit(s->has_piece, x, y)){
        if(get_bit(s->is_white, x, y)){
            return GOBANG_STATE_WHITE_STEP;
        }
        return GOBANG_STATE_BLACK_STEP;
    }
    return 0;
}


int gobang_get_last_piece(gobang_state* s, int* x, int* y){
    if(s->last_step_count == 0)return -1;
    *x = s->last_step[s->last_step_count - 1].x;
    *y = s->last_step[s->last_step_count - 1].y;
    return 0;
}

//悔棋
int gobang_undo(gobang_state* s){
    int x, y;
    if(s->last_step_count == 0)return -1;
    s->last_step_count --;
    x = s->last_step[s->last_step_count].x;
    y = s->last_step[s->last_step_count].y;
    unset_bit(s->has_piece, x, y);
    if(s->state == GOBANG_STATE_BLACK_STEP || s->state == GOBANG_STATE_WHITE_WIN){
        s->state = GOBANG_STATE_WHITE_STEP;
    }else{
        s->state = GOBANG_STATE_BLACK_STEP;
    }
    return 0;
}

